# coding:utf8
import requests
import re
from lxml import etree
import random
import parsel


# 发起请求
def post_request(url=None):

    # 网址
    # url = 'http://www.dianping.com/shop/H9FNPpCqj1Tu98oD/review_all'
    # 请求头
    headers = {
        'Connection': 'keep-alive',
        'Host': 'www.dianping.com',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
        'Cookie': 'fspop=test; cy=1305; cye=huaiyang; _lxsdk=181aa7ad838c8-03fafa0ef11406-26021a51-154ac4-181aa7ad838c8; _lxsdk_cuid=181aa7ad838c8-03fafa0ef11406-26021a51-154ac4-181aa7ad838c8; _hc.v=492fd566-e5d1-725b-d6a8-df2ece63c8df.1656422587; s_ViewType=10; dper=35e3a1c591e245be209e4ebd5830e3c4171c5892b304e9c3b7213e1902b777f0716c0a139036324bf35dae94b160677df284f026b15ff9fcb176396112fd73b1; ll=7fd06e815b796be3df069dec7836c3df; Hm_lvt_602b80cf8079ae6591966cc70a3940e7=1656422587,1656467024,1656542870; _lx_utm=utm_source=Baidu&utm_medium=organic; WEBDFPID=8454y3x6vx225zy811w7uwy7v9208x76818ywuuv73097958119z9029-1656836472401-; Hm_lpvt_602b80cf8079ae6591966cc70a3940e7=1656913554; _lxsdk_s=181c7aad8b1-48e-530-49e||203'
    }


    # 获取响应
    r = requests.get(url=url, headers=headers)
    # r = requests.get(url=url, headers=headers, proxies=ip)
    # print(f'能用的ip地址：{ip}')
    print("访问成功")
    # 获取文本
    text = r.text.encode("gbk", "ignore").decode("gbk", "ignore")  # 解决报错双重严格限制
    return text


# print(post_request())

# 获取店铺名称
def get_shop_headinfo():
    item = {}
    text = post_request()
    # print(text)
    select = parsel.Selector(text)
    # shop_name = re.search('<h1 class="shop-name">(.*?)</h1>', text)
    shop_name = select.xpath('//*[@id="review-list"]/div[2]/div[1]/div[1]/h1/text()').get()
    item['shop_name'] = shop_name
    # 匹配获取到 星级的 星星的 div 类名 （类名中的最后一个数字，表示的就是星级的分数--10 分代表的就是一个满星星）
    shop_star_class_name = \
        select.xpath('//*[@id="review-list"]/div[2]/div[1]/div[2]/div/div[1]/span[1]/@class').extract()[0]
    # print(shop_star_class_name)   # 输出示例：star star_50

    # 提取店铺评分
    shop_star = shop_star_class_name.split(" ")[1].split("_")[-1]
    item['shop_star'] = shop_star

    # 评价数
    comment_num_text = select.xpath('//*[@id="review-list"]/div[2]/div[1]/div[2]/span[1]/text()').get()

    # print(comment_num_text)
    # 输出示例：234条评价
    comment_num = comment_num_text.split("条")[0]
    item['comment_num'] = comment_num

    # 人均消费
    average_cost_text = select.xpath('//*[@id="review-list"]/div[2]/div[1]/div[2]/span[2]/text()').get()
    # print(average_cost_text)
    # 输出示例：人均：234元
    average_cost = average_cost_text.split("：")[1].split("元")[0]
    item['average_cost'] = average_cost

    return item


# print(get_shop_headinfo())


# 评论加密 svg 以及对应的字典信息
# 这里得到的 css 文件  为全局的 css
# 获取css背景信息, 并返回映射字典内容url以及背景类字典坐标信息
def comment_css_info():
    text = post_request()
    css_url = re.search('<link rel="stylesheet" type="text/css" href="(//s3plus.meituan.*?)"', text)[1]
    # 拼接css_rul
    total_css_url = 'http:' + css_url
    # 得到 css 代码
    text1 = post_request(total_css_url)
    # 正则匹配类名、x坐标、y坐标
    background_name_list = re.findall(r'\.(.*?){background:-(.*?).0px -(.*?)\.0px;}', text1)
    # 存放类信息字典
    back_dict = {}
    for item in background_name_list:
        # key: 类名  [x, y]
        back_dict[item[0]] = [item[1], item[2]]

    # 映射字典内容url
    svg_url = re.findall(r'svgmtsi\[class\^="sdf"\].*?background-image: url\((.*?)\)', text1)
    total_svg_url = 'http:' + svg_url[0]
    # 返回两个关键信息
    return total_svg_url, back_dict


print(comment_css_info())


# 地址加密 svg 以及对应的字典信息
# 这里得到的 css 文件  为全局的 css
# 获取css背景信息, 并返回映射字典内容url以及背景类字典坐标信息
def address_css_info():
    text = post_request()
    css_url = re.search('<link rel="stylesheet" type="text/css" href="(//s3plus.meituan.*?)"', text)[1]
    # 拼接css_rul
    total_css_url = 'http:' + css_url
    # 得到 css 代码
    text1 = post_request(total_css_url)
    # 正则匹配类名、x坐标、y坐标
    background_name_list = re.findall(r'\.(.*?){background:-(.*?).0px -(.*?)\.0px;}', text1)
    # 存放类信息字典--地址信息
    back_dict = {}
    for item in background_name_list:
        # key: 类名  [x, y]
        back_dict[item[0]] = [item[1], item[2]]

    # 映射字典内容url
    svg_url = re.findall(r'svgmtsi\[class\^="hzp"\].*?background-image: url\((.*?)\)', text1)
    total_svg_url = 'http:' + svg_url[0]
    # 返回两个关键信息
    return total_svg_url, back_dict


# 地址加密 svg 以及对应的字典信息
# 这里得到的 css 文件  为全局的 css
# 获取css背景信息, 并返回映射字典内容url以及背景类字典坐标信息
def num_css_info():
    text = post_request()
    css_url = re.search('<link rel="stylesheet" type="text/css" href="(//s3plus.meituan.*?)"', text)[1]
    # 拼接css_rul
    total_css_url = 'http:' + css_url
    # 得到 css 代码
    text1 = post_request(total_css_url)
    # 正则匹配类名、x坐标、y坐标
    background_name_list = re.findall(r'\.(.*?){background:-(.*?).0px -(.*?)\.0px;}', text1)
    # 存放类信息字典
    back_dict = {}
    for item in background_name_list:
        # key: 类名  value:[x, y]
        back_dict[item[0]] = [item[1], item[2]]

    # 映射字典内容url
    svg_url = re.findall(r'svgmtsi\[class\^="ves"\].*?background-image: url\((.*?)\)', text1)
    total_svg_url = 'http:' + svg_url[0]
    # 返回两个关键信息
    return total_svg_url, back_dict


# TODO: 增加函数的参数，可以返回 评论，地址，数字 对应的 映射文本（可能数字的映射文本的获取会有一点点的特殊）
# 映射文本获取 -- 一行文本
def mapping_text(flag, kind='comment'):
    """
    得到映射文本
    :param flag:
    :param kind: 要映射文本的种类名称 comment address num  只能是三者其中之一
    :return: 字典  y_text_dict:{key:y, value:高度为y的这一行的文本 }
    """
    url = comment_css_info()[0]
    if kind == 'address':
        # 请求链接 -- svg 文字字典链接
        url = address_css_info()[0]
    elif kind == 'num':
        # 请求链接 -- svg 文字字典链接
        url = num_css_info()[0]
    else:
        print("请输入正确的要映射文本的种类名称 comment address num")
        return

    print(url)
    # 返回文本 -- svg 文字字典
    text2 = post_request(url)

    # 检查网页源代码，根据对应的 svg 格式来传入 flag 的值

    # 第一种字体格式：每一行是一个 text 标签
    # <text x="0" y="41">...(省略了本行的文字)...</text>
    # x 代表的是一行字中某一个字的索引（从 0 开始），
    # y 代表的是高度->得到某一行文本
    if flag == '2':
        select = parsel.Selector(text2)
        # 找到所有的text标签
        content_list = select.css('text')
        # 存放信息滴字典
        y_text_dict = {}
        for content in content_list:
            # 获取文本 某一行文本
            # print(content.css("text::text").get())
            line_text = content.css("text::text").get()
            # 获取y属性
            # print(content.css("text::attr(y)").get())
            line_y = content.css("text::attr(y)").get()
            # 保存到字典当中（高度y当作建，内容文本当作值）
            y_text_dict[line_y] = line_text
            '''
             y_text_dict:{key:y, value:高度为y的这一行的文本 }
            '''
        return y_text_dict

    #  第二种svg格式: svg 的网页检查，每一行是一个 path 标签
    #  <path id="1" d="M0 45 H600">...(省略了本行的文字)...</path>
    # id 代表的是一行字中某一个字的索引（从 1 开始），
    # d 属性中间的 数值 代表的就相当于 第一种svg格式中的 y 属性（即高度）-> 得到一行文本
    if flag == '1':
        select = parsel.Selector(text2)
        # 找到所有的path标签
        path_list = select.css('path')
        print("第二种svg格式")
        # print(path_list)
        y_list = []
        for path in path_list:
            # 获取高度
            y = path.css("path::attr(d)").get().split(' ')[1]
            y_list.append(y)
        line_text_list = []
        content_list = select.css('textPath')
        for content in content_list:
            # 获取文本
            line_text = content.css("textPath::text").get()
            line_text_list.append(line_text)
        # 存放信息滴字典
        y_text_dict = {}
        for k, v in zip(y_list, line_text_list):
            y_text_dict[k] = v
        return y_text_dict


# TODO: 传入要返回的字体信息列表对应的类名列表（可以实现 评论，地址，数字三者）
def svg_character(name_class_list, y_text_dict):
    """
    获取svg字体信息，对应文本
    :param kind: 要映射文本的种类名称 comment address num  只能是三者其中之一
    :param name_class_list: 加密字的类名列表
    :param y_text_dict: 得到的映射文本字典 （y 为 key, 高度为 y 的这一行文本为 value）
    :return: 返回 类名列表对应的字体信息列表（位置一一对应）
    """

    # if kind == 'comment':
    #     back_dict = comment_css_info()
    # elif kind == 'address':
    #     back_dict = address_css_info()
    # elif kind == 'num':
    #     back_dict = num_css_info()
    # else:
    #     print("请输入正确的要映射文本的种类名称 comment address num")
    #     return

    # 定义一个空的列表，用来存储传入的 name_class_list 类名列表对应的字体信息
    character_list = []
    for name_class in name_class_list:
        try:
            y = back_dict[name_class][1]
            # print(f'y:{y}')
            # 拿到这个类名对应的字体的x轴
            x = back_dict[name_class][0]
        except:
            print('error')

        # 找对应的行数
        for index in y_text_dict.keys():
            # 第一个比y大的行数
            # print(index)
            if int(index) > int(y):
                # 计算x的位置, 14为字体的大小
                position = int(int(x) / 14)
                # print(position)
                # 从index这一行获取这个字体信息
                find_it = y_text_dict[index][position]
                # print(y_text_dict[index])
                # print(find_it, end="->")
                character_list.append(find_it)
                # 找到后跳出
                break
    # print(character_list)
    return character_list


# 获取评论
def search_comment(background_info, r_text):
    # 获取文本
    text = post_request()
    # print(text)
    # 转化为xpath对象
    tree = etree.HTML(text)
    # 拿到所有的评论列表svg类名
    svg_info_list = tree.xpath(r'.//div[@class="main-review"]/div[@class="review-truncated-words"]/svgmtsi/@class')
    # print(svg_info_list)
    # TODO: 正则匹配div class="review-truncated-words">下面滴那一段东西,然后直接拼接起来
    # TODO: 使用字体映射替换<svgmtsi class="vtejr"></svgmtsi>这一堆东西，然后在聚合
    # comment_list = re.findall(r'<div class="review-truncated-words">((.*?)<svgmtsi class="(.*?)"></svgmtsi>).*?<div class="more-words">', text, re.S)
    '''
    通过对页面的分析，标签 <div class="main-review">下面 有两个有关评论的标签（两者是只能有其一的关系）
    1. 折叠的评价 -> truncated-words ==> 点击 展开评价 才可以看到全部评价（可以看到下面的这个 div）
    <div class="review-truncated-words">评论内容<div class="more-words"><a>展开评价</a></div></div>
    2.展开的评价(真正的所有评价内容) ==> 点击 收起评价 就可以看到折叠的评价，即上面的 div
    <div class="review-words">评论内容<div class="less-words"><a>收起评价</a></div></div>
    
    所以，要爬取一条评论的所有内容，应该爬取 review-words 这个标签中的内容
    对于评价内容并不是很长的评价，只有一个 <div class="review-words"> 标签 尤其注意，这种评论 没有收起评价标签 ==> 在进行数据提取的时候要注意 匹配正则不可以 以 <div class="more-words"> 结尾！！！
    '''
    # comment_list = re.findall(r'<div class="review-truncated-words">(.*?)<div class="more-words">', text, re.S)
    comment_list = re.findall(r'<div class="review-words">(.*?)</div>', text, re.S)
    new_comment_list = []
    # 开始处理每一段评论
    try:
        for comment in comment_list:
            # print(comment)
            # 获取class_info属性,返回类名
            class_info = re.findall(r'<svgmtsi class="(.*?)"></svgmtsi>', comment, re.S)
            # 将svg标签映射为字体获得列表
            character_list = svg_character(class_info, r_text)
            # print(character_list)
            # 开始替换评论中的<svg巴拉巴拉
            for character, name in zip(character_list, class_info):
                # print(character, name)
                # 替换字体评论
                comment = comment.replace(f'<svgmtsi class="{name}"></svgmtsi>', character).strip().replace('&#x20;',
                                                                                                            ' ') \
                    .replace('&#x0A;', ' ')
                # 去掉图片链接脏数据
                comment = re.sub(r'<img class="emoji-img" src=".*?" alt=""/>', '', comment)
            # 添加
            new_comment_list.append(comment)
    except:
        print("error")
    print(new_comment_list)
    # print(comment_list)
    return new_comment_list


def save_comment(info_list):
    with open('大众点评信息.txt', 'w', encoding='utf8') as f:
        for info in info_list:
            f.writelines(info)
            f.write('\n')


if __name__ == '__main__':
    # ip池
    # ip_list = [
    #     {'http': 'http://8.135.103.42:80'},
    #     {'http': 'http://112.80.248.75:80'},
    #     {'http': 'http://113.214.13.1:1080'},
    # ]
    #
    # # 随机获取ip
    # ip = ip_list.pop(random.randint(0, len(ip_list) - 1))
    # print(f'正在使用的ip地址：{ip}')
    # 背景信息
    # back_dict = comment_css_info()[1]
    #
    # # 如果是textLength属性标签, 就传1, 否则传2
    # # 获得映射字典
    # r_text_dict = mapping_text('2', 'comment')
    # print(back_dict)
    # print(r_text_dict)
    #
    # # 获取评论
    # comment_list = search_comment(back_dict, r_text_dict)
    #
    # # 保存评论
    # save_comment(comment_list)

    base_url = 'http://www.dianping.com/shop/H9FNPpCqj1Tu98oD/review_all'

    with open('data.csv') as f:
        f_csv = csv.DictReader(f)
    for row in f_csv:
        # 输出一行数据
        print(row)
        # 输出一行数据中的键值为 key 的对应的 value 值
    print(row['key'])